/*

The MIT License

Copyright (c) 2009 Valentin valyard Simonov ( vv@lent.in, http://va.lent.in ).

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

*/

package ru.valyard.smart9slice
{
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.geom.Rectangle;

	/**
	 * This class is abstract, use classes which extend it.
	 */
	public class Abstract9Slice extends Sprite
	{
		
		// containers for elements
		protected var containerTL: Sprite;
		protected var containerT: Sprite;
		protected var containerTR: Sprite;
		protected var containerL: Sprite;
		protected var containerC: Sprite;
		protected var containerR: Sprite;
		protected var containerBL: Sprite;
		protected var containerB: Sprite;
		protected var containerBR: Sprite; 
		
		// elements
		private var _tl: DisplayObject;
		private var _t: DisplayObject;
		private var _tr: DisplayObject;
		private var _l: DisplayObject;
		private var _c: DisplayObject;
		private var _r: DisplayObject;
		private var _bl: DisplayObject;
		private var _b: DisplayObject;
		private var _br: DisplayObject;
		
		// which parts should be distorted while scaled
		private var _topScaled: Boolean = false;
		private var _leftScaled: Boolean = false;
		private var _centerScaled: Boolean = false;
		private var _rightScaled: Boolean = false;
		private var _bottomScaled: Boolean = false;
		private var _topLeftScaled: Boolean = false;
		private var _topRightScaled: Boolean = false;
		private var _bottomLeftScaled: Boolean = false;
		private var _bottomRightScaled: Boolean = false;
		
		// scale grid parameters
		private var _topDistance: int;
		private var _rightDistance: int;
		private var _bottomDistance: int;
		private var _leftDistance: int;
		private var _width: int = 0;
		private var _height: int = 0;
		
		private var initialized: Boolean = false;
		
		public function Abstract9Slice(  )
		{
			super();
		}
		
		/**
		 * Width of the object
		 * Minimum width is (size of left object) + (size of right object)
		 */
		public override function get width(): Number
		{
			return _width;
		}
		 
		public override function set width( value:Number ): void
		{
			_width = int( value );
			if ( _width < _leftDistance + _rightDistance )
				_width = _leftDistance + _rightDistance;
			redraw();
		}
		
		/**
		 * Height of the object
		 * Minimum height is (size of top object) + (size of bottom object)
		 */
		public override function get height(): Number
		{
			return _height;
		}
		
		public override function set height( value:Number ): void
		{
			_height = int( value );
			if ( _height < _topDistance + _bottomDistance )
				_height = _topDistance + _bottomDistance;
			redraw();
		}
		
		/**
		 * Top distance of scale grid
		 */
		public function get topDistance(): int
		{
			return _topDistance;	
		}
		
		public function set topDistance( value: int ): void
		{
			_topDistance = value;
			redraw();	
			redrawConsts();
		}
		
		/**
		 * Right distance of scale grid
		 */
		public function get rightDistance(): int
		{
			return _rightDistance;	
		}
		
		public function set rightDistance( value: int ): void
		{
			_rightDistance = value;	
			redraw();
			redrawConsts();
		}
		
		/**
		 * Bottom distance of scale grid
		 */
		public function get bottomDistance(): int
		{
			return _bottomDistance;	
		}
		
		public function set bottomDistance( value: int ): void
		{
			_bottomDistance = value;
			redraw();
			redrawConsts();	
		}
		
		/**
		 * Left distance of scale grid
		 */
		public function get leftDistance(): int
		{
			return _leftDistance;	
		}
		
		public function set leftDistance( value: int ): void
		{
			_leftDistance = value;	
			redraw();
			redrawConsts();
		}
		
		/**
		 * If set to true top area is scaled instead of duplicated
		 */
		public function get topScaled(): Boolean
		{
			return _topScaled;
		}
		
		public function set topScaled( value: Boolean ): void
		{
			_topScaled = value;
			redraw();
		}
		
		/**
		 * If set to true left area is scaled instead of duplicated
		 */
		public function get leftScaled(): Boolean
		{
			return _leftScaled;
		}
		
		public function set leftScaled( value: Boolean ): void
		{
			_leftScaled = value;
			redraw();
		}
		
		/**
		 * If set to true center area is scaled instead of duplicated
		 */
		public function get centerScaled(): Boolean
		{
			return _centerScaled;
		}
		
		public function set centerScaled( value: Boolean ): void
		{
			_centerScaled = value;
			redraw();
		}
		
		/**
		 * If set to true right area is scaled instead of duplicated
		 */
		public function get rightScaled(): Boolean
		{
			return _rightScaled;
		}
		
		public function set rightScaled( value: Boolean ): void
		{
			_rightScaled = value;
			redraw();
		}
		
		/**
		 * If set to true bottom area is scaled instead of duplicated
		 */
		public function get bottomScaled(): Boolean
		{
			return _bottomScaled;
		}
		
		public function set bottomScaled( value: Boolean ): void
		{
			_bottomScaled = value;
			redraw();
		}
		
		/**
		 * If set to true top left area is scaled instead of duplicated
		 */
		public function get topLeftScaled(): Boolean
		{
			return _topLeftScaled;
		}
		
		public function set topLeftScaled( value: Boolean ): void
		{
			_topLeftScaled = value;
			redraw();
			redrawConsts();
		}
		
		/**
		 * If set to true top right area is scaled instead of duplicated
		 */
		public function get topRightScaled(): Boolean
		{
			return _topRightScaled;
		}
		
		public function set topRightScaled( value: Boolean ): void
		{
			_topRightScaled = value;
			redraw();
			redrawConsts();
		}
		
		/**
		 * If set to true bottom left area is scaled instead of duplicated
		 */
		public function get bottomLeftScaled(): Boolean
		{
			return _bottomLeftScaled;
		}
		
		public function set bottomLeftScaled( value: Boolean ): void
		{
			_bottomLeftScaled = value;
			redraw();
			redrawConsts();
		}
		
		/**
		 * If set to true bottom right area is scaled instead of duplicated
		 */
		public function get bottomRightScaled(): Boolean
		{
			return _bottomRightScaled;
		}
		
		public function set bottomRightScaled( value: Boolean ): void
		{
			_bottomRightScaled = value;
			redraw();
			redrawConsts();
		}
		
		/**
		 * Top left element object
		 */
		public function get topLeft(): DisplayObject
		{
			return _tl;
		}
		
		/** 
		 * Top element object
		 */
		public function get top(): DisplayObject
		{
			return _t;
		}
		
		/**
		 * Top right element object
		 */
		public function get topRight(): DisplayObject
		{
			return _tr;
		}
		
		/**
		 * Left element object
		 */
		public function get left(): DisplayObject
		{
			return _l;
		}
		
		/**
		 * Center element object
		 */
		public function get center(): DisplayObject
		{
			return _c;
		}
		
		/**
		 * Right element object
		 */
		public function get right(): DisplayObject
		{
			return _r;
		}
		
		/**
		 * Bottom left element object
		 */
		public function get bottomLeft(): DisplayObject
		{
			return _bl;
		}
		
		/**
		 * Bottom element object
		 */
		public function get bottom(): DisplayObject
		{
			return _b;
		}
		
		/**
		 * Bottom right element object
		 */
		public function get bottomRight(): DisplayObject
		{
			return _br;
		}
		
		/**
		 * Current scale-9 rectangle
		 */
		public override function get scale9Grid(): Rectangle
		{
			return new Rectangle( _leftDistance, _topDistance, _width - _leftDistance - _rightDistance, _height - _topDistance - _bottomDistance );
		}
		
		public override function set scale9Grid( innerRectangle: Rectangle ): void
		{
			// can't set at runtime
		}
		
		/**
		 * Returns original scale9Grid and clears it
		 */
		protected function getOriginalScale9Grid(): Rectangle
		{
			var grid: Rectangle = super.scale9Grid;
			super.scale9Grid = null;
			return grid;	
		}
		
		/**
		 * Returns original width
		 */
		protected function getOriginalWidth(): int
		{
			return super.width;	
		}
		
		/**
		 * Returns original height
		 */
		protected function getOriginalHeight(): int
		{
			return super.height;	
		}
		
		/**
		 * Redraws the object when resized
		 */
		protected function redraw(): void
		{
			var mW: int = this.width - this.leftDistance - this.rightDistance;
			var mH: int = this.height - this.topDistance - this.bottomDistance;
		
			fill( containerT, this.top, this.topScaled, mW, this.topDistance );
			fill( containerL, this.left, this.leftScaled, this.leftDistance, mH );
			fill( containerC, this.center, this.centerScaled, mW, mH );
			fill( containerR, this.right, this.rightScaled, this.rightDistance, mH );
			fill( containerB, this.bottom, this.bottomScaled, mW, this.bottomDistance );
			
			containerTL.x = 0;
			containerTL.y = 0;
			containerT.x = this.leftDistance;
			containerT.y = 0;
			containerTR.x = this.width - this.rightDistance;
			containerTR.y = 0;
			containerL.x = 0;
			containerL.y = this.topDistance;
			containerC.x = this.leftDistance;
			containerC.y = this.topDistance;
			containerR.x = this.width - this.rightDistance;
			containerR.y = this.topDistance;
			containerBL.x = 0;
			containerBL.y = this.height - this.bottomDistance;
			containerB.x = this.leftDistance;
			containerB.y = this.height - this.bottomDistance;
			containerBR.x = this.width - this.rightDistance;
			containerBR.y = this.height - this.bottomDistance;
		}
		
		/**
		 * Redraws parts which are not changed during width/height change
		 */
		protected function redrawConsts(): void
		{
			fill( containerTL, this.topLeft, this.topLeftScaled, this.leftDistance, this.topDistance );
			fill( containerTR, this.topRight, this.topRightScaled, this.rightDistance, this.topDistance );
			fill( containerBL, this.bottomLeft, this.bottomLeftScaled, this.leftDistance, this.bottomDistance );
			fill( containerBR, this.bottomRight, this.bottomRightScaled, this.rightDistance, this.bottomDistance );
		}
		
		/**
		 * Clear selected sprite
		 */
		protected function clear( s: Sprite ): void
		{
			s.graphics.clear();
			while ( s.numChildren )
				s.removeChildAt( 0 );
		}
		
		/**
		 * Fill selected sprite
		 * param	container	A sprite to fill
		 * param 	data		Bitmap to fill with
		 * param 	scale		Should it be scaled or duplicated
		 * param	w			Width
		 * param	h			Height
		 */
		protected function fill( container: Sprite, data: DisplayObject, scale: Boolean, w: int, h: int ): void
		{
			
		}
		
		/**
		 * Initializes the object
		 * param	initObj		Object with parameters
		 * param 	width		Width
		 * param	height		Height
		 * param 	rect		Scale9Grid rectangle
		 */
		protected function init( initObj: Object, width: int, height: int, rect: Rectangle ): void
		{
			initParts( initObj );
			initDimensions( width, height, rect );	
			
			while ( this.numChildren )
				this.removeChildAt( 0 );
			
			containerTL = new Sprite();
			containerT = new Sprite();
			containerTR = new Sprite();
			containerL = new Sprite();
			containerC = new Sprite();
			containerR = new Sprite();
			containerBL = new Sprite();
			containerB = new Sprite();
			containerBR = new Sprite();
			
			addChild( containerTL );
			addChild( containerT );
			addChild( containerTR );
			addChild( containerL );
			addChild( containerC );
			addChild( containerR );
			addChild( containerBL );
			addChild( containerB );
			addChild( containerBR );
		}
		
		protected function initParts( init: Object ): void
		{
			if ( init["tl"] && init["tl"] is DisplayObject )
			{
				_tl = init["tl"];
				_topLeftScaled = false;
			}else if ( init["tl_"] && init["tl_"] is DisplayObject )
			{
				_tl = init["tl_"];
				_topLeftScaled = true;
			}else
			{
				_tl = new Sprite();
				_topLeftScaled = true;
			}
			
			if ( init["t"] && init["t"] is DisplayObject )
			{
				_t = init["t"];
				_topScaled = false;
			}else if ( init["t_"] && init["t_"] is DisplayObject )
			{
				_t = init["t_"];
				_topScaled = true;
			}else
			{
				_t = new Sprite();
				_topScaled = true;
			}
			
			if ( init["tr"] && init["tr"] is DisplayObject )
			{
				_tr = init["tr"];
				_topRightScaled = false;
			}else if ( init["tr_"] && init["tr_"] is DisplayObject )
			{
				_tr = init["tr_"];
				_topRightScaled = true;
			}else
			{
				_tr = new Sprite();
				_topRightScaled = true;
			}
			
			if ( init["l"] && init["l"] is DisplayObject )
			{
				_l = init["l"];
				_leftScaled = false;
			}else if ( init["l_"] && init["l_"] is DisplayObject )
			{
				_l = init["l_"];
				_leftScaled = true;
			}else
			{
				_l = new Sprite();
				_leftScaled = true;
			}
			
			if ( init["c"] && init["c"] is DisplayObject )
			{
				_c = init["c"];
				_centerScaled = false;
			}else if ( init["c_"] && init["c_"] is DisplayObject )
			{
				_c = init["c_"];
				_centerScaled = true;
			}else
			{
				_c = new Sprite();
				_centerScaled = true;
			}
			
			if ( init["r"] && init["r"] is DisplayObject )
			{
				_r = init["r"];
				_rightScaled = false;
			}else if ( init["r_"] && init["r_"] is DisplayObject )
			{
				_r = init["r_"];
				_rightScaled = true;
			}else
			{
				_r = new Sprite();
				_rightScaled = true;
			}
			
			if ( init["bl"] && init["bl"] is DisplayObject )
			{
				_bl = init["bl"];
				_bottomLeftScaled = false;
			}else if ( init["bl_"] && init["bl_"] is DisplayObject )
			{
				_bl = init["bl_"];
				_bottomLeftScaled = true;
			}else
			{
				_bl = new Sprite();
				_bottomLeftScaled = true;
			}
			
			if ( init["b"] && init["b"] is DisplayObject )
			{
				_b = init["b"];
				_bottomScaled = false;
			}else if ( init["b_"] && init["b_"] is DisplayObject )
			{
				_b = init["b_"];
				_bottomScaled = true;
			}else
			{
				_b = new Sprite();
				_bottomScaled = true;
			}
			
			if ( init["br"] && init["br"] is DisplayObject )
			{
				_br = init["br"];
				_bottomRightScaled = false;
			}else if ( init["br_"] && init["br_"] is DisplayObject )
			{
				_br = init["br_"];
				_bottomRightScaled = true;
			}else
			{
				_br = new Sprite();
				_bottomRightScaled = true;
			}
		}
		
		protected function initDimensions( w: int, h: int, rect: Rectangle ): void
		{
			_width = w;
			_height = h;
			
			_leftDistance = int(rect.x);
			_topDistance = int(rect.y);
			_rightDistance = _width - _leftDistance - int(rect.width);
			_bottomDistance = _height - _topDistance - int(rect.height); 
		}
		
		/**
		 * Returns true if the object has specially named children on stage
		 */
		protected function hasAppropriateNamedChildren(): Boolean
		{
			var names: Array = ["tl", "t", "tr", "l", "c", "r", "bl", "b", "br"];
			var s: String;
			for each ( s in names )
			{
				if ( getChildByName( s ) || getChildByName( s.toUpperCase() ) || getChildByName( s+"_" ) || getChildByName( s.toUpperCase()+"_" ) )
					return true;
			}
			return false;
		}
		
		/**
		 * Returns object width considering vMask helper
		 */
		protected function getItemWidth( obj: DisplayObject ): int
		{
			if ( !obj )
				return 0;
			
			var vMask: DisplayObject;
			if ( obj is DisplayObjectContainer && ((vMask = (obj as DisplayObjectContainer).getChildByName( "vMask" ))
				|| (vMask = (obj as DisplayObjectContainer).getChildByName( "visibilityMask" ) ))  )
			{
				return vMask.width;
			}else{
				return obj.width;
			}
		}
		
		/**
		 * Returns object height considering vMask helper
		 */
		protected function getItemHeight( obj: DisplayObject ): int
		{
			if ( !obj )
				return 0;
			
			var vMask: DisplayObject;
			if ( obj is DisplayObjectContainer && ((vMask = (obj as DisplayObjectContainer).getChildByName( "vMask" ))
				|| (vMask = (obj as DisplayObjectContainer).getChildByName( "visibilityMask" ) ))  )
			{
				return vMask.height;
			}else{
				return obj.height;
			}
		}
	}
}